////////////////////////////////////////////////
// DESCRIPTION:
//     Support classes for drivers to access
//     embedded pointers in control handler buffers
//
// Legal Notices:
//     Copyright (c) 2008, Telliam Consulting, LLC.
//     All rights reserved.
//
//     Redistribution and use in source and binary forms, with or without modification,
//     are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright notice,
//       this list of conditions and the following disclaimer in the documentation
//       and/or other materials provided with the distribution.
//
//     * Neither the name of Telliam Consulting nor the names of its contributors
//       may be used to endorse or promote products derived from this software
//       without specific prior written permission.
//
// Disclaimer:
//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
//     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
//     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
//     IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
//     INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
//     DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//     LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
//     OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
//     OF THE POSSIBILITY OF SUCH DAMAGE.  
//
#pragma once

namespace FusionWare
{
    namespace DFX
    {
        // do the forward refernce dance required by C++. [Sigh...]
        // put all the docs here so it need not be duplicated for each version

        ///////////////////////////////////////////////////////
        // Description: 
        //    template class for embedded buffers used in
        //    worker threads.
        //
        // Remarks:
        //    The OS cannot automatically map a pointer embedded
        //    in a buffer provided in a PSL call (Driver/service)
        //    therefore the driver must do the mapping. 
        //  
        //    This class manages the mapping in a type safe manner
        //    when used synchronously in the PSL call thread. 
        //
        // Input:
        //    Type of data pointed to by the embedded pointer.
        //
        // Example:
        //   [in the open context]
        //    
        //   struct MyData {
        //      const TCHAR* pBuffer;
        //      const size_t BuffLen;
        //      ...
        //   };
        //
        //   [in xxx_IoControl]
        //   CallerBuffer<const TCHAR> pMappedBuffer;
        //   if(InBufSize < sizeof(MyData) || NULL == pInBuf)
        //       return FALSE;
        //
        //   MYData* pMyData = reinterpret_cast<MyData*>(pInBuf);
        //   if(!MapCallerBuffer(pMappedBuffer, pMyData->pBuffer, pMyData->BuffLen))
        //   { report failure and return }
        //   ... Party on the pMappedBuffer
        //
        template<typename T> class CallerBuffer;

        ///////////////////////////////////////////////////////
        // Description: 
        //    This method maps the pointer provided by the caller
        //
        // Output:
        //    CallerBuffer - newly mapped buffer pointer
        // 
        // Input: 
        //    pBuf  - Embedded pointer to map
        //    Count - Count of elements in the buffer
        //
        // Remarks: 
        //    This method maps a pointer that is embedded in a
        //    buffer provided to a driver/service/PSL so that
        //    it can be accessed safely.
        //
        //    This method will ASSERT if pBuf is already mapped.
        //    For maximum compatibility with future versions 
        //    you cannot re-map a pointer already mapped by the
        //    OS. You only need to map pointers embedded in a
        //    data structure passed in a buffer not the actual
        //    pInBuf nor pOutBuf - the OS already maps those 
        //    buffers on your behalf. 
        //
        // Implementation Note:
        //    For Macallan there is no need to worry about unmapping
        //    an already mapped pointer since the mapping doesn't
        //    actually allocate any actual resources in Macallan.
        //
        template<typename T> bool MapCallerBuffer(CallerBuffer<T>& CallerBuffer, void* pBuf, size_t Count=1);

        template<typename T> bool UnmapCallerBuffer(CallerBuffer<T>& CallerBuffer);

        ///////////////////////////////////////////////////////
        // Description: 
        //    template class for embedded buffers used in
        //    worker threads.
        //
        // Remarks:
        //    The OS cannot automatically map a pointer embedded
        //    in a buffer provided in a PSL call (Driver/service)
        //    therefore the driver must do the mapping. 
        //  
        //    This class manages the mapping in a type safe manner
        //    when used Asynchronously by a worker thread. 
        //
        // Input:
        //    Type of data pointed to by the embedded pointer.
        //
        // Example:
        //   [in the open context]
        //    
        //   struct MyData {
        //      const TCHAR* pBuffer;
        //      const size_t Len;
        //      ...
        //   };
        //
        //   AsynchCallerBuffer<const TCHAR> pMappedBuffer;
        //
        //   [in xxx_IoControl]
        //   if(pInBufSize < sizeof(MyData) || NULL == pInBuf)
        //       return FALSE;
        //
        //   MYData* pMyData = ((MyData*)pInBuf);
        //   if(!MapCallerBuffer(pMappedBuffer, pMyData->pBuffer, pMyData->Len))
        //   { deal with error and return }
        //
        //   [in worker thread]
        //   {  
        //        // the destructor for this instance will restore permissions
        //        // when it goes out of scope.
        //        GrantThreadBufferAccess<> bufferPermissions(pMappedBuffer);
        //
        //        //... party on the data
        //        DoSomethingUseful(pMappedBuffer, GetCount(pMappedBuffer));
        //
        //   }  
        template<typename T> class AsynchCallerBuffer;
        template<typename T> bool MapCallerBuffer(AsynchCallerBuffer<T>& CallerBuffer, void* pBuf, size_t Count=1);
        template<typename T> bool UnmapCallerBuffer(AsynchCallerBuffer<T>& CallerBuffer);

        ///////////////////////////////////////////////////////
        // Description: 
        //    template class for granting access to data in
        //    worker threads.
        //
        // Input:
        //    T - Type of object to protect
        //
        // Remarks:
        //    The constructor sets the access permisions and
        //    destructor releases them. Thus the thread has 
        //    access to the data throughout the life time of
        //    an instance of this class.
        //
        //    Do NOT create more than one instance of this class
        //    per thread scope. As there is no way to control
        //    correct resetting of the permissions in CE 5.0. 
        //
        // See Also:
        //    AsynchCallerBuffer
        //
        template<typename T> class GrantThreadBufferAccess;

        ///////////////////////////////////////////////////////
        // Description: 
        // 
        // Input: 
        //    T   - type of value to get count of
        //    Val - Value to get count of. 
        //
        // Returns: 
        //    size_t - count of vaues in the  value
        //     
        // Remarks: 
        //    This template maintains pointer semantics for
        //    smart buffer pointers. The only constraint on 
        //    type T is that it must have a data member called
        //    Count that is accesible to this function. (usually
        //    accomplished via a friend declaration)
        //
        template<class T>
        size_t GetCount(const T& Val) { return Val.Count; }

        template<typename T>
        class CallerBuffer
        {
        private:
            T* p;
            size_t Count;
            bool Mapped;

            // these are inentionally private and never defined
            // to maintain smart pointer semantics and prevent
            // use of delete on the smart pointer.
            void* operator new(size_t);
            void operator delete(void* );
            
            // use friend functions to maintain pointer semantics consistently

            friend bool MapCallerBuffer<T>(CallerBuffer<T>& CallerBuf, void* pBuf, size_t Count);
            friend bool UnmapCallerBuffer<T>(CallerBuffer<T>& CallerBuf);
            friend bool IsMapped(CallerBuffer<T>& CallerBuf);
            friend size_t GetCount<T>(const T& Val);

        public:
            CallerBuffer() 
                : Count(0)
                , p(NULL)
            {
            }

            bool operator!()  { return p!=NULL; }
            T* operator->()   { return p; }
            T operator*()     { return *p; }
            operator T*()     { return p; }

            T& operator[](int index)
            { 
                ASSERT(index < this->Count);
                return this->p[index];
            }
        };

        template<typename T>     
        bool MapCallerBuffer(CallerBuffer<T>& CallerBuf, void* pBuf, size_t Count/*=1*/)
        {
            HRESULT hr = CeOpenCallerBuffer(reinterpret_cast<void**>(&CallerBuf.p), pBuf, sizeof(T)*Count, ARG_I_PTR, FALSE);
            return SUCCEEDED(hr);
        }

        template<typename T>
        bool UnmapCallerBuffer(CallerBuffer<T>& CallerBuf)
        {
            HRESULT hr = CeCloseCallerBuffer( reinterpret_cast<void*>(&CallerBuf.p), pBuf, sizeof(T)*Count, ARG_I_PTR, FALSE);
            return SUCCEEDED(hr);
        }

#if SUPPORT_ASYNCHCALLERBUFFER
        template<typename T>
        class AsynchCallerBuffer
        {
        private:
            T* pMarshalled;
            CallerBuffer<T> pMapped;

            // these are inentionally private and never defined
            // to maintain pointer semantics.
            void* operator new(size_t);
            void operator delete(void* );
            
            // use friend functions to maintain pointer semantics consistently

            friend bool MapCallerBuffer<T>(AsynchCallerBuffer<T>& CallerBuf, const CallerBuffer<T>& CallerBuf);
            friend bool UnmapCallerBuffer<T>(AsynchCallerBuffer<T>& CallerBuf);
            friend size_t GetCount<T>(const T& Val);
            friend GrantThreadBufferAccess<T>;

        public:
            AsynchCallerBuffer() 
                : Count(0)
                , pMarshalled(NULL)
            {
            }

            ~AsynchCallerBuffer()
            {
                UnmapCallerBuffer(*this);   
            }
            
            bool operator!()   { return pMarshalled!=NULL; } 
            T* operator->()    { return pMarshalled; }
            T operator*()      { return *pMarshalled; }
            operator T*()      { return pMarshalled; }
            T& operator[](int index) { ASSERT(index < this->Count); return this->pMarshalled[index]; }
        };
        
        template <class T>
        class GrantThreadBufferAccess
        {
            GrantThreadBufferAccess(const AsynchCallerBuffer<T>& pBuf)
            {
            }

            ~GrantThreadBufferAccess()
            {
            }
        };

        ///////////////////////////////////////////////////////
        // Description: 
        //    This method maps the pointer provided by the caller
        //
        // Input: 
        //    pBuf  - Embedded pointer to map
        //    Count - Count of elements in the buffer
        //
        // Remarks: 
        //    This method maps a pointer that is embedded in a
        //    buffer provided to a driver/service/PSL so that
        //    it can be accessed safely.
        //
        //    This method will ASSERT if pBuf is already mapped.
        //    For maximum compatibility with future versions 
        //    you cannot re-map a pointer already mapped by the
        //    OS. You only need to map pointers embedded in a
        //    data structure pased in a buffer not the actual
        //    pInBuf nor pOutBuf - the OS already maps those 
        //    buffers on your behalf. 
        //
        //   You *MUST* call this from the PSL thread
        //   (i.e. in DeviceIoControl) and NOT in the worker
        //   thread.
        //
        template<typename T> 
        bool MapCallerBuffer(AsynchCallerBuffer<T>& CallerBuf, void* pBuf, size_t Count/*=1*/, UINT32 Flags = ARG_I_PTR)
        {
            if(!MapCallerBuffer(CallerBuf.pMappped, pBuf, sizeof(T)*Count))
                return false;

            HRESULT hr = CeAllocAsynchronousBuffer(&CallerBuf.pMarshalled, CallerBuf.pMapped.p, Count * sizeof(t),  Flags);
            if(FAILED(hr))
            {
                UnmapCallerBuffer(CallerBuf.pMapped);
                return false;
            }
            CallerBuf.Flags = Flags;
            return true;
        }

        template<typename T> 
        bool UnmapCallerBuffer(AsynchCallerBuffer<T>& CallerBuffer)
        {
            // complain about un mapping a pointer that is not mapped. 
            ASSERT(CallerBuf.pMarhalled != NULL);

            HRESULT hr = CeFreeAsynchronousBuffer(CallerBuf.pMarshalled, CallerBuf.pMapped.p, CallerBuf.Count * sizeof(t), CallerBuf.Flags);
            if(FAILED(hr))
                return false;
            
            return UnmapCallerBuffer(CallerBuf.pMapped);
        }
#endif
    }
}
